package com.study.reflect.demo.base;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.junit.Test;

/**
* @Name: ReflectDemo
* @Description: 反射测试类
* 说明：
* 	反射：就是通过class字节码文件对象，去使用该字节码文件类中的成员变量、构造方法、成员方法等。
* 	要使用反射，首先必须得到类对应的class字节码文件对象，即得到Class类的对象。
* 	Class类：Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
* 			枚举是一种类，注释是一种接口。每个数组属于被映射为 Class 对象的一个类，所有具有相同元素类型和维数的数组都共享该 Class 对象。
* 			基本的 Java 类型（boolean、byte、char、short、int、long、float 和 double）和关键字 void 也表示为 Class 对象。
* 			当然也包括自己编写的类和接口。
* 			Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。 
* 	Class类的常用方法：
* 		1、获取字节码文件对象
* 			获取Class类的实例（即指定字节码文件对象）的方式：
* 				1、使用Object类的getClass()方法；
* 				2、使用数据类型的静态属性class；
* 				3、使用Class类成员方法中的静态方法：static Class<?> forName(String className)  参数className为：对应字节码文件的包.类路径（可以提前配置到配置文件中，常用）
* 		2、获取字节码文件对象的构造方法对象
* 			Constructor<?>[] getConstructors()   返回[某些]Constructor对象的数组，这些对象反映此 Class对象所表示的类的所有[公共构造方法]。
* 				返回Class对象所表示的类的所有[公共构造方法]的Constructor[]数组。
* 				说明：Constructor类提供关于类的单个构造方法的信息以及对它的访问权限。 
* 			Constructor<?>[] getDeclaredConstructors()   返回 [所有]Constructor对象的一个数组，这些对象反映此 Class对象表示的类声明的[所有构造方法]。
* 			Constructor<T> getConstructor(Class<?>... parameterTypes)  返回[一个]Constructor对象，它反映此 Class对象所表示的类的[指定公共构造方法]。
* 				说明：参数Class<?>... parameterTypes表示：待获取构造方法的构造参数个数及对应数据类型的class字节码文件对象。
* 			Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回[一个]Constructor对象，它反映此 Class对象所表示的类的[指定所有构造方法]。
* 				说明：参数Class<?>... parameterTypes表示：待获取构造方法的构造参数个数及对应数据类型的class字节码文件对象。
* 		3、创建对象实例
* 			Constructor类：Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。 
* 				T newInstance(Object... initargs)  使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例，并用指定的初始化参数初始化该实例。
* 				public void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
* 		4、获取字节码文件对象的成员变量对象
* 			Field[] getFields()  获取所有公共成员变量对象
* 			Field getField(String name)  获取指定的公共成员变量对象
* 			Field[] getDeclaredFields()  获取所有成员变量对象
* 			Field getDeclaredField(String name)  获取指定成员变量对象
* 		5、操作成员变量
* 			Field类：Field 提供有关类或接口的单个字段的信息，以及对它的动态访问权限。
* 			取消 Java语言访问权限检查
* 				public void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
* 			设置成员变量的值：
* 				void set(Object obj, Object value)  将指定对象变量上此 Field 对象表示的字段设置为指定的新值，如果底层字段的类型为基本类型，则对新值进行自动解包。 
* 			获取成员变量的值：
* 				Object get(Object obj)  返回指定对象上此 Field 表示的字段的值。
*		6、获取字节码文件对象的成员方法对象
*			Method[] getMethods()  获取所有自己以及父类的公共方法对象数组
*			Method getMethod(String name, Class<?>... parameterTypes)   获取指定自己或父类的公共方法对象
*				参数：
*					String name	方法名称
*					Class<?>... parameterTypes 方法形参数据类型对象
*			Method[] getDeclaredMethods()  获取所有自己的方法对象数组
*			Method getDeclaredMethod(String name, Class<?>... parameterTypes)  获取指定自己的方法对象数组
*				参数：
*					String name	方法名称
*					Class<?>... parameterTypes 方法形参数据类型对象
*		7、操作成员方法
*			Method类：Method 提供关于类或接口上单独某个方法（以及如何访问该方法）的信息。所反映的方法可能是类方法或实例方法（包括抽象方法）。 
*			取消 Java语言访问权限检查
* 				public void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
* 			调用成员方法：
* 				Object invoke(Object obj, Object... args)  对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
* 					参数：
* 						Object obj 方法所属对象
* 						Object... args	方法的实参
* @Author: 李超	lc_jack@foxmail.com
* @CreateDate: 2017-1-17 下午4:22:34 
* @Version: V1.0
 */
public class ReflectDemo {
	
	/**
	* @Name: getClassObject
	* @Description: 获取Class类实例的三种方式
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午4:42:35
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void getClassObject() throws ClassNotFoundException {
		//1、使用Object类的getClass()方法
		Person p1 = new Person() ;
		Class<Person> c1 = (Class<Person>) p1.getClass() ;
		//2、使用数据类型的静态属性class
		Person p2 = new Person() ;
		Class<Person> c2 = Person.class ;
		//使用Class类成员方法中的静态方法：static Class<?> forName(String className)
		Class<Person> c3 = (Class<Person>) Class.forName("com.study.reflect.Person") ;
		//获取到同一个Person.class字节码文件对象
		System.out.println(c1 == c2);
		System.out.println(c1 == c3);
		System.out.println(c2 == c3);
	}
	
	/**
	* @Name: getObjectInstance01
	* @Description: 获取字节码文件对象的构造方法对象，并通过无参构造方法创建对象实例
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午5:40:25
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void getObjectInstance01() throws Exception {
		//1、获取Person.class字节码文件对象
		Class<Person> clazz = (Class<Person>) Class.forName("com.study.reflect.Person") ;
		//2、获取Person.class字节码文件中定义的构造方法对象
		//获取所有的公共构造方法对象
		Constructor[] conArray = clazz.getConstructors() ;
		for (Constructor con : conArray) {
			System.out.println(con);
		}
		//获取所有构造方法对象
		Constructor[] dconArray = clazz.getDeclaredConstructors() ;
		for (Constructor con : dconArray) {
			System.out.println(con);
		}
		//获取指定的构造方法对象
		//获取无参构造方法对象
		Constructor<Person> con1 = clazz.getConstructor() ;
		System.out.println(con1);
		//如何获取有参构造方法对象呢？
		//根据获取到的构造方法对象，创建Person对象实例
		Person p = con1.newInstance() ;
		System.out.println(p);
	}
	
	/**
	* @Name: getObjectInstance02
	* @Description: 获取字节码文件对象的有参构造方法对象，并创建对象实例
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午5:57:42
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void getObjectInstance02() throws Exception {
		//获取字节码文件对象
		Class clazz = Class.forName("com.study.reflect.Person") ;
		//使用字节码文件对象，获取字节码文件中定义的有参构造方法
		//public Person(String name, int age, String address)
		Constructor<Person> con = clazz.getConstructor(String.class, int.class, String.class) ;
		//使用获取到的有参构造方法对象，创建对象实例
		Person person = con.newInstance("张三", 21, "北京") ;
		System.out.println(person);
	}
	
	/**
	* @Name: getObjectInstance03
	* @Description: 获取字节码文件对象的私有构造方法对象，并通过私有有参构造方法对象创建对象实例
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午5:57:42
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void getObjectInstance03() throws Exception {
		//获取字节码文件对象
		Class clazz = Class.forName("com.study.reflect.Person") ;
		//使用字节码文件对象，获取字节码文件中定义的私有构造方法
		//private Person(String name)
		//方式一：NoSuchMethodException
//		Constructor<Person> con = clazz.getConstructor(String.class) ;
		//方式二：IllegalAccessException
		Constructor<Person> con = clazz.getDeclaredConstructor(String.class) ;
		//暴力访问：setAccessible()
		//			——将此对象的 accessible标志设置为指示的布尔值。值为true则指示反射的对象在使用时应该取消Java语言访问检查。
		con.setAccessible(true) ;
		//使用获取到的私有构造方法对象，创建对象实例
		Person per = con.newInstance("李四") ;
		System.out.println(per);
	}
	
	/**
	* @Name: setFieldValue
	* @Description: 获取字节码文件对象的成员变量对象，并通过成员变量对象操作成员变量的值
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午6:32:31
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void setFieldValue() throws Exception {
		//获取字节码文件对象
		Class clazz = Class.forName("com.study.reflect.Person") ;
		//获取构造方法对象
		//获取无参构造：public Person() {}
		Constructor con = clazz.getDeclaredConstructor() ;
		//设置不检查访问权限
		con.setAccessible(true) ;
		//创建对象实例
		Person per = (Person) con.newInstance() ;
		System.out.println(per);
		//获取成员变量对象
		//获取public String address ;
		Field addressField = clazz.getDeclaredField("address") ;
		addressField.setAccessible(true) ;
		addressField.set(per, "北京") ;
		System.out.println(per);
		//获取private String name
		Field nameField = clazz.getDeclaredField("name") ;
		nameField.setAccessible(true) ;
		nameField.set(per, "张三") ;
		System.out.println(per);
		//获取int age
		Field ageField = clazz.getDeclaredField("age") ;
		ageField.setAccessible(true) ;
		ageField.set(per, 21) ;
		System.out.println(per);
	}
	
	/**
	* @Name: getMethodInfo
	* @Description: 获取字节码文件对象的成员方法，并调用执行该方法
	* @Author: 李超	lc_jack@foxmail.com
	* @Version: V1.0
	* @CreateDate: 2017-1-17 下午7:05:29
	* @Parameters: 
	* @Return: void
	 */
	@Test
	public void getMethodInfo() throws Exception {
		//获取字节码文件对象
		Class clazz = Class.forName("com.study.reflect.Person") ;
		//获取构造方法对象
		Constructor con = clazz.getDeclaredConstructor() ;
		//取消访问权限检查
		con.setAccessible(true) ;
		//创建对象实例
		Person per = (Person) con.newInstance() ;
		//获取成员变量方法对象
		//获取公共无参无返回值成员方法public void show()
		Method showMethod = clazz.getDeclaredMethod("show") ;
		//取消权限访问检查
		showMethod.setAccessible(true) ;
		//执行该方法
		showMethod.invoke(per) ;
		System.out.println("=============================");
		//获取公共有参无返回值成员方法：public void method(String s)
		Method methodMethod = clazz.getDeclaredMethod("method", String.class) ;
		//取消访问权限检查
		methodMethod.setAccessible(true) ;
		//执行该方法
		methodMethod.invoke(per, "访问测试！") ;
		System.out.println("=============================");
		//获取公共有参有返回值成员方法：public String getString(String s, int i)
		Method getStringMethdod = clazz.getDeclaredMethod("getString", String.class, int.class) ;
		//取消访问权限检查
		getStringMethdod.setAccessible(true) ;
		//执行该方法，获取方法的返回值
		String result = (String) getStringMethdod.invoke(per, "测试", 001) ;
		System.out.println(result);
		System.out.println("=============================");
		//获取私有无参无返回值成员方法：private void function()
		Method functionMethod = clazz.getDeclaredMethod("function") ;
		//取消权限访问检查
		functionMethod.setAccessible(true) ;
		//执行该方法
		functionMethod.invoke(per) ;
		System.out.println("=============================");
	}
	
	
}














